package utils

/**
  定时的时间轮
  放弃使用, 使用utils_timewheel
*/

import (
	"sync/atomic"
	"time"
)

type Job func()

type taskrec struct {
	key int32
	job Job
}

type IntervalTimeWheel struct {
	key               int32
	slotNum           uint16
	currentPos        uint16
	jobUseGo          bool
	ticker            *time.Ticker
	slots             []*DList // 时间轮槽
	taskMap           map[int32]*DElement
	addTaskChannel    chan taskrec // 新增任务channel
	removeTaskChannel chan int32   // 删除任务channel
	stopChannel       chan bool    // 停止定时器channel
}

func NewIntervalTimeWheel(secs uint16, jobUseGo bool) *IntervalTimeWheel {
	rval := &IntervalTimeWheel{
		key:               0,
		slotNum:           secs,
		jobUseGo:          jobUseGo,
		currentPos:        0,
		ticker:            time.NewTicker(time.Second),
		slots:             make([]*DList, secs),
		taskMap:           make(map[int32]*DElement),
		addTaskChannel:    make(chan taskrec),
		removeTaskChannel: make(chan int32),
		stopChannel:       make(chan bool),
	}
	rval.initSlots()
	return rval
}

// Start 启动时间轮
func (this *IntervalTimeWheel) Start() {
	this.ticker = time.NewTicker(time.Second)
	GoFunc2(this.innerStart)
}

func (this *IntervalTimeWheel) innerStart() {
	for {
		select {
		case <-this.ticker.C:
			this.tickHandler()
		case task := <-this.addTaskChannel:
			this.innerAddTask(&task)
		case key := <-this.removeTaskChannel:
			this.innerRemoveTask(key)
		case <-this.stopChannel:
			this.ticker.Stop()
			return
		}
	}
}

func (this *IntervalTimeWheel) Stop() {
	this.stopChannel <- true
}

func (this *IntervalTimeWheel) tickHandler() {
	defer PanicHandler()
	l := this.slots[this.currentPos]
	this.scanAndRunTask(l)
	if this.currentPos == this.slotNum-1 {
		this.currentPos = 0
	} else {
		this.currentPos++
	}
}

func (this *IntervalTimeWheel) initSlots() {
	for i := 0; i < int(this.slotNum); i++ {
		this.slots[i] = NewDList()
	}
}

func (this *IntervalTimeWheel) innerRemoveTask(key int32) {
	ele := this.taskMap[key]
	if ele != nil {
		ele.RemoveFromDList()
	}
	delete(this.taskMap, key)
}

func (this *IntervalTimeWheel) AddTask(job Job) (key int32) {
	key = atomic.AddInt32(&this.key, 1)
	this.addTaskChannel <- taskrec{key: key, job: job}
	return
}

func (this *IntervalTimeWheel) RemoveTask(key int32) {
	this.removeTaskChannel <- key
}

func (this *IntervalTimeWheel) innerAddTask(task *taskrec) {
	pos := int(this.currentPos+this.slotNum) % int(this.slotNum)
	ele := this.slots[pos].PushBack(task.job)
	this.taskMap[task.key] = ele
}

func (this *IntervalTimeWheel) scanAndRunTask(l *DList) {
	for e := l.Front(); e != nil; {
		task := e.Value.(Job)
		if this.jobUseGo {
			GoFunc2(func() {
				task()
			})
		} else {
			task()
		}
		e = e.Next()
	}
}
